#include "Prngd.h"
#include "BBS.h"
#include "signal.h"
#include <iostream>


Prngd::Prngd(Prngd_State state)
{
	this->state = state;
	useRandom = false; //kann noch per parameter auf true gestellt werden
	bbs = new BBS();
	iounit = new IOUnit();
	mpz_init(s);
	mpz_init(s2);
	mpz_init(output_bits);
	bitCount = 0;
	output_file = new std::string(OUTPUT_STDFILE); //nur zum testen, normal entfernen
	prngd_switch = PRNGD_BBS;
	seed_part2 = NULL;

}


Prngd::~Prngd()
{
	mpz_clear(s);
	mpz_clear(s2);
	mpz_clear(output_bits);
	delete bbs;

	if(output_file != NULL)
		delete output_file;

	if(seed_part2 != NULL)
		delete seed_part2;

}


char* Prngd::work(int argc, char** argv)
{
	char* result;
	int status = 0;

	status = readParameters(argc, argv);

	if(status == 0)
	{
		//generate our seed
		//get some random data from dev/(u)random and xor it with our s from the user input
		seed_part2 = getRndData(bitCount, useRandom);
		mpz_set_str(s2, seed_part2, 2);

		#ifdef DEBUG_MODE
			std::cout << "\nSeed_part1: " << s;
			std::cout << "\nSeed_part2: " << s2;
		#endif

		mpz_xor(s, s , s2);
		mpz_init(s2); //Overwrite s2 with zeros

		#ifdef DEBUG_MODE
			std::cout << "\nSeed: " << s;
		#endif


		switch(prngd_switch)
		{
			case PRNGD_NONE:
				status = -1;
				break;
			case PRNGD_BBS:
				result = bbs->generateBits(s, bitCount, false);
				if(state == LOCAL_ST)
					iounit->writeToFile((char*)output_file->c_str(), result, bitCount/8 + bitCount%8, true);
				break;
			case PRNGD_ELSE:
				break;
			case PRNGD_FBBS:
				result = bbs->generateBits(s, bitCount, true);
				if(state == LOCAL_ST)
					iounit->writeToFile((char*)output_file->c_str(), result, bitCount/8 + bitCount%8, true);
				break;
			default:
				status = -1;
				break;
		}
	}
	return result;
}



char* Prngd::getRndData(int numberOfBits, bool useRandom)
{
	char* pbBuffer = new char[numberOfBits+1]; //+1 for NULL termination, will be deleted trough seed_part2 pointer with the destructor of prngd
	int readBits = 0;
	char temp;
	std::ifstream devRandom;


	if(!useRandom)
		devRandom.open(u_random_file, std::ifstream::in);
	else
		devRandom.open(random_file, std::ifstream::in);

	if(devRandom.is_open())
	{
		while(readBits < numberOfBits && devRandom.good())
		{
			temp = devRandom.get();
			//convert the 8-Bit Char into a binary array of 8 fields
			for(int i= 0 ; i < 8 ; i++)
			{
				if((temp& 0x80) >> 7 == 1)
					pbBuffer[readBits+i] = '1';
				else
					pbBuffer[readBits+i] = '0';

				temp = temp << 1;
			}
			readBits+=8;
		}
		devRandom.close();
	}

	pbBuffer[numberOfBits] = '\0'; //pretty important. Without termination the gmp lib will not be able to convert the data to binary

	#ifdef DEBUG_MODE
		std::cout << "\nBinary from dev/(u)random/: ";
		for(int i = 0; i < numberOfBits; i++)
			std::cout << pbBuffer[i];
	#endif


	return pbBuffer;
}


/*Eingabe fuer prngd soll sein:
 argv[1] = Bevorzugter prngd (bbs oder...) -bbs
 argv[2] = anzahl der zu erzeugenden bits
 argv[3] = -s
 argv[4] = seed
 argv[5] = -o
 argv[5] = dateiname fuer output
*/
int Prngd::readParameters(int param_count, char** params)
{

	for(int i = 1; i < param_count; i++)
	{
		/*favorite prngd is blum blum shub and the next arg is
		 the number of random bits to generate.*/
		if(strcmp(params[i], "-bbs") == 0)
		{
			prngd_switch = PRNGD_BBS;
			i++;
			if(i < param_count)
			{
				sscanf(params[i], "%d", &bitCount);
				if(bitCount <= 0)
				{
					std::cout << "No number of bits to generate where given.\n";
					return -1;
				}
			}
			else
			{
				std::cout << "No number of bits to generate where given.\n";
				return -1;
			}
		}
		else if(strcmp(params[i], "-fbbs") == 0)
		{
			prngd_switch = PRNGD_FBBS;
			i++;
			if(i < param_count)
			{
				sscanf(params[i], "%d", &bitCount);
				if(bitCount <= 0)
				{
					std::cout << "No number of bits to generate where given.\n";
					return -1;
				}
			}
			else
			{
				std::cout << "No number of bits to generate where given.\n";
				return -1;
			}
		}
		/*Seed Parameter, which can be a big Integer*/
		else if(strcmp(params[i], "-s") == 0)
		{
			i++;
			if(i < param_count && strcmp(params[i], "")) //check if there is a value behind -s
				mpz_set_str(s, params[i], 10);
			else
			{
				mpz_set_ui(s, 0); //no value found
				std::cout << "No seed value found after -s.\n";
				return -1;
			}
		}
		/* Filename, where we want to write out random Bits to.*/
		else if(strcmp(params[i], "-o") == 0)
		{
			i++;
			if(i < param_count && params[i][0] != '-' && strcmp(params[i], ""))
				output_file = new std::string(params[i]);
			else
			{
				i--;
				std::cout << "No valid filename after Parameter -o.\n";
				std::cout << "The standard file prngd_out will be used.\n";
				output_file = new std::string(OUTPUT_STDFILE);
			}
		}
		// show help
		else if(strcmp(params[i], "--help") == 0)
		{
			std::cout << "\nAufruf: prngd GENERATOR OPTION"
			<< "\nGeneriert eine gewünschte Zahl an Zufallsbits mittels BBS \ns"
			<< "oder eines determinisitschen Zufallszahlengenerators und \n"
			<< "speichert diese binaer in einer Datei.\n"
			<< "\n GENERATOR: "
			<< "\n -bbs xxxx \t bevorzugt Blum Blum Shub Generator verwenden und xxxx Zufallsbits generieren. "
			<< "\n -xxx \t bevorzugt den anderen Generator verwenden.\n"
			<< "\n OPTION: "
			<< "\n -s xxxx \t weißt prngd an die Eingabe xxxx zur Generierung des Seeds zu verwenden."
			<< "\n -o dateiname \t speichert die generierten Zufallsbits in der angegeben Datei."
			<< "\n --help \t zeigt diese Hilfe an.\n\n";


		}
		else if(strcmp(params[i], "") == 0)
		{
			//ignore, this maybe deleted -i arguments from Prngd_Server
		}
		else
		{
			std::cout << "Unknown Parameter: " << params[i] << "\n"
					  << "Use --help to show known parameters.\n";
			return -1;
		}
	}

	if(bitCount == 0 || s == 0)
		return -1;

	return 0;
}


